///////////////////////////////////////////////
// This file is part of NoLifeStory.         //
// Please see Global.h for more information. //
///////////////////////////////////////////////
#pragma once

class NetworkSession {
public:
	NetworkSession(string IP, unsigned short port);
	~NetworkSession() {
		WSACleanup();
	}
	
	void SendPacket(const PacketWriter &packet, bool encrypt = true);
	void SendData(const unsigned char* buffer, int length, bool encrypt = true);
	DWORD ReceiveAndHandlePackets();
	void InitializeHeaderHandlers();

	int8_t GetMapleServerType() const { return mapletype; }
	short GetMapleServerVersion() const { return mapleversion; }

	// Encryption stuff
	void SetRecvIV(unsigned char* IV) { SetIV(IV, IV_RECV); }
	void SetSendIV(unsigned char* IV) { SetIV(IV, IV_SEND); }
	void SetIV(unsigned char* newIV, unsigned char* oldIV) {
		for (auto i = 0; i < 4; i++) 
			memcpy(oldIV + i * 4, newIV, 4);
	}
	void DecryptPacket(unsigned char* data, int length);
	void EncryptPacket(unsigned char* data, int length);
	void CreateHeader(unsigned char* header, short length);

	static uint8_t ror(uint8_t value, int times);
	static uint8_t rol(uint8_t value, int times);
	void NextIV(unsigned char *oldiv);

	static unsigned short GetLengthFromHeader(unsigned char* header);
	static const int BUFFER_LEN = 4000; // 2KB for a packet?
	static const int HEADER_LEN = 4;
	static const int HEADER_NO_ENC_LEN = 2;
	static DWORD _stdcall CreateReceiveThread(void *param);
private:
	void ReceiveHeader(unsigned char* buffer);
	void ReceiveData(unsigned char* buffer, int length);

	unsigned char IV_RECV[16];
	unsigned char IV_SEND[16];
	SOCKET sock;
	bool use_encryption;
	bool header;
	short mapleversion;
	int8_t mapletype;
	map<header_t, void (*)(PacketReader &)> headers;
};

extern NetworkSession *ns;

inline uint8_t NetworkSession::rol(uint8_t value, int times) {
	int highbit;
	for (auto i = 0; i < times; i++) {
		highbit = ((value & 0x80) ? 1 : 0);
		value <<= 1;
		value |= highbit;
	}
	return value;
}

inline uint8_t NetworkSession::ror(uint8_t value, int times) {
	int lowbit;
	for (auto i = 0; i < times; i++) {
		lowbit = ((value & 1) ? 1 : 0);
		value >>= 1;
		value |= (lowbit << 7);
	}
	return value;
}